home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sounds Terrific 2
/
Sounds Terrific II (1996)(Weird Science)(Disc 1 of 2)[Amiga-PC].iso
/
archives
/
amiga
/
audio_handler.lha
/
audio.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-08-01
|
23KB
|
785 lines
/*
* Audio Handler
*
* Copyright (c) 1992,1995 by Martin Brenner, martin@ego.oche.de
*
* THIS INFORMATION IS PROVIDED "AS IS"; NO WARRANTIES ARE MADE.
* ALL USE IS AT YOUR OWN RISK, AND NO LIABILITY OR
* RESPONSIBILITY IS ASSUMED.
*
* This handler is partially based on port-handler by Andy Finkel.
* Some insight about DOS and the SAS C Compiler was gained from
* the Guru Book by Ralph Babel.
*
* For each new invocation of this handler a new process is started,
* just like the console handler. Options after the colon ':' are
* evaluated and Audio output is done with double buffering.
*
* This is an example for a handler where incoming DOS packets are
* NOT the same length as messages sent to the exec device. Instead,
* data is buffered and sent to the device au bloc.
*
* $Header: Work:Progs/aud/RCS/audio.c,v 1.4 1995/08/01 03:04:48 martin Exp martin $
*
* $Log: audio.c,v $
* Revision 1.4 1995/08/01 03:04:48 martin
* Bug fixes for version 1.0 beta
*
* Revision 1.3 1995/07/19 10:04:04 martin
* first release version, 1.0 beta
*
* Revision 1.2 1995/07/04 20:00:33 martin
* almost completely rewritten, now finally working
*
* Revision 1.1 92/12/22 17:01:47 martin
* Initial revision
*
*
*
*/
#include <exec/memory.h>
#include <exec/lists.h>
#include <exec/errors.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <devices/audio.h>
/* Hack, because registerized BeginIO() doesn't seem to exist in small.lib */
#define BeginIO __stdargs BeginIO
#include <proto/exec.h>
#undef BeginIO
#include "audio.h"
/* debugging on the serial port */
#define DEBUG
#define DPRINTF KPrintF
#define QTOUPPER(c) ((c)>='a'&&(c)<='z'?(c)-'a'+'A':(c))
#undef BADDR
#define BADDR(x) ((CPTR)((LONG)x << 2))
/*
* global vars
*
* since several invocations (processes) of the handler use the SAME
* global data segment, process specific data MUST be allocated or
* created on the stack. ExecBase can safely be global, though.
*/
extern struct ExecBase *__far AbsExecBase;
struct ExecBase *SysBase;
/* Prototypes */
BOOL checkReadyQueue(struct AudioState *as);
BOOL checkIOBHandling(struct AudioState *as);
void swapPtrs(struct AudioState *as);
void allocIO(struct IOAudio *iob, struct AudioOptions *ao);
void freeIO(struct IOAudio *iob, struct AudioOptions *ao);
void lockIO(struct IOAudio *iob, struct AudioOptions *ao);
void abortIO(struct IOAudio *iob, struct AudioOptions *ao);
void writeIO(struct IOAudio *iob, struct AudioBuffer *buffer, struct AudioOptions *ao);
void returnpkt(struct DosPacket *packet, LONG res1, LONG res2, struct AudioOptions *ao);
int strncmpi(char *str1, char *str2, int n);
BOOL parseArgs(struct AudioOptions *ao, int len, char *str);
LONG getLong(int *len, char **ptr);
/*
* quark()
*
* entry point for the handler.
*/
void __saveds quark(void)
{
UBYTE *openstring; /* BCPL String to Handler Device Name */
BOOL error = 1;
struct AudioState state; /* This struct contains the complete information about Audio IO */
struct DeviceNode *devnode; /* our device node is passed in parm pkt Arg3 */
struct DosPacket *packet; /* temporary packet handle, as received from the calling process */
struct MsgPort *devport; /* MessagePort of handler process */
struct Process *process; /* Process ID of handler process */
struct Message *message; /* temporary message handle */
/* First, initialize SysBase, so we may call exec functions */
SysBase = AbsExecBase;
process = (struct Process *)FindTask(NULL);
devport = &process->pr_MsgPort;
WaitPort(devport); /* Wait for startup message */
message = (struct Message *)GetMsg(devport);
packet = (struct DosPacket *) message->mn_Node.ln_Name;
openstring = (UBYTE *)BADDR(packet->dp_Arg1);
devnode = (struct DeviceNode *)BADDR(packet->dp_Arg3);
/* Set default options, currently any channel */
state.AS_Options.AO_Priority = 0;
state.AS_Options.AO_NumChannels = 4;
state.AS_Options.AO_Channels = "\x1\x2\x4\x8";
state.AS_Options.AO_BufferSize = AUDIOBUFSIZE;
state.AS_Options.AO_Period = DEFAULT_PERIOD;
state.AS_Options.AO_Volume = DEFAULT_VOLUME;
state.AS_Options.AO_Debug = 0;
/* Seek for ':' and interpret everything after as options */
parseArgs(&(state.AS_Options), openstring[0], openstring+1);
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("S%ld", state.AS_Options.AO_Debug);
#endif
/*
* create several IO blocks, Messageport and open the device
* we dont create another port, but use our own port
*/
state.AS_IO1 = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
if (!state.AS_IO1)
goto handler_exit;
state.AS_IO2 = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
if (!state.AS_IO2)
goto handler_exit;
state.AS_AllocIO = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
if (!state.AS_AllocIO)
goto handler_exit;
state.AS_LockIO = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
if (!state.AS_LockIO)
goto handler_exit;
state.AS_CurrentIO = state.AS_IO1;
state.AS_NextIO = state.AS_IO2;
state.AS_AllocIO->ioa_Request.io_Message.mn_ReplyPort = devport;
/* link a dummy packet */
state.AS_DummyPacket.dp_Type = ACTION_WRITE_RETURN;
state.AS_AllocIO->ioa_Request.io_Message.mn_Node.ln_Name = (UBYTE *)&(state.AS_DummyPacket);
state.AS_BytesCopied = 0; /* A counter for bytes copied from DOS packet to Audio buffer */
/* allocate chip mem for audio double buffer, initialize all buffer variables */
state.AS_Buffer1.AB_Data = AllocMem(state.AS_Options.AO_BufferSize, MEMF_CHIP);
state.AS_Buffer1.AB_Size = state.AS_Options.AO_BufferSize;
state.AS_Buffer1.AB_End = 0;
state.AS_Buffer1.AB_InUse = 0;
if (!state.AS_Buffer1.AB_Data)
goto handler_exit;
state.AS_Buffer2.AB_Data = AllocMem(state.AS_Options.AO_BufferSize, MEMF_CHIP);
state.AS_Buffer2.AB_Size = state.AS_Options.AO_BufferSize;
state.AS_Buffer2.AB_End = 0;
state.AS_Buffer2.AB_InUse = 0;
if (!state.AS_Buffer2.AB_Data)
goto handler_exit;
/* Current buffer is buffer currently played */
/* Next buffer is buffer currently being filled with sound data */
state.AS_CurrentBuffer = &(state.AS_Buffer1);
state.AS_NextBuffer = &(state.AS_Buffer2);
/* Initialize the DOS packet input queue */
NewList(&(state.AS_ReadyQueue));
/* Open audio.device, DON'T allocate any channels yet */
if (error = OpenDevice(AUDIONAME, 0, (struct IORequest *)state.AS_AllocIO, 0)) {
returnpkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE, &(state.AS_Options));
goto handler_exit;
}
state.AS_AllocState = ALLOCSTATE_UNUSED;
state.AS_LockState = LOCKSTATE_UNUSED;
state.AS_OpenForOutput = FALSE;
/* devnode->dn_Task = devport; nope, start one task for each invocation */
/* Finished with startup parameter packet...send back...*/
returnpkt(packet, DOSTRUE, packet->dp_Res2, &(state.AS_Options));
/*
* The Main Event Loop:
*
* We have the following incoming messages:
*
* ACTION_FINDOUTPUT user process opens our file handle for output
* ACTION_WRITE play sound request coming from the user process
* ACTION_END the file handle is closed -> we will be closed
* ACTION_IS_FILESYSTEM is the handler a filesystem? NO
* return from ADCMD_ALLOCATE coming from the audio.device
* return from CMD_WRITE coming from the audio.device
* return from ADCMD_FREE coming from the audio.device
* the last three will be labeled with ACTION_WRITE_RETURN on our message port.
* Therefore, a dummy packet is linked with the IO-Requests.
*
* ADCMD_ALLOCATE might block if a channel is not available
* ACTION_WRITE may block, if the buffer is full
* ACTION_END may block, if there is still sound playing
*
* Audio return messages have a dummy dos packet in their ln_Name field,
* which has a type of ACTION_WRITE_RETURN.
*/
do {
WaitPort(devport);
/* evaluate incoming packets */
while ((message = (struct Message *) GetMsg(devport)) != 0) {
packet = (struct DosPacket *) message->mn_Node.ln_Name;
switch (packet->dp_Type) {
case ACTION_FINDOUTPUT:
/* The device is opened here. We try to allocate requested channel(s) */
state.AS_FileHandle = (struct FileHandle *) BADDR(packet->dp_Arg1);
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("F%ld", state.AS_Options.AO_Debug);
#endif
if (state.AS_OpenForOutput) {
returnpkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE, &(state.AS_Options));
}
else {
state.AS_OpenForOutput = DOSTRUE;
state.AS_FileHandle->fh_Port = (struct MsgPort *)DOSTRUE; /* Interactive */
state.AS_FileHandle->fh_Arg1 = 42; /* handler-private info */
returnpkt(packet, DOSTRUE, 0L, &(state.AS_Options));
}
break;
case ACTION_END: /* Close request */
state.AS_OpenForOutput = FALSE;
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("E%ld", state.AS_Options.AO_Debug);
#endif
returnpkt(packet, DOSTRUE, 0L, &(state.AS_Options));
break;
case ACTION_WRITE: /* Write Request */
/* put new write request in the waiting queue */
AddTail(&(state.AS_ReadyQueue), (struct Node *)message);
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("W%ld", state.AS_Options.AO_Debug);
#endif
if (state.AS_AllocState == ALLOCSTATE_UNUSED) {
/* allocate channel(s) */
/* request new key */
state.AS_AllocIO->ioa_AllocKey = 0;
allocIO(state.AS_AllocIO, &(state.AS_Options));
state.AS_AllocState = ALLOCSTATE_ALLOCATING;
}
break;
case ACTION_IS_FILESYSTEM: /* Is handler a filesystem? */
/* It isn't! */
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("I%ld", state.AS_Options.AO_Debug);
#endif
returnpkt(packet, DOSFALSE, 0L, &(state.AS_Options));
break;
case ACTION_WRITE_RETURN:
/* IO Request came back - check which one */
switch (((struct IORequest *)message)->io_Command) {
case ADCMD_ALLOCATE:
/* allocation has finished, initialize iobs with key */
*state.AS_IO1 = *state.AS_AllocIO;
*state.AS_IO2 = *state.AS_AllocIO;
*state.AS_LockIO = *state.AS_AllocIO;
state.AS_AllocState = ALLOCSTATE_ALLOCATED;
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("ra%ld", state.AS_Options.AO_Debug);
#endif
/* lock the allocated channel */
lockIO(state.AS_LockIO, &(state.AS_Options));
state.AS_LockState = LOCKSTATE_LOCKED;
break;
case CMD_WRITE:
/* check, which buffer is free again */
if ((struct IOAudio *)message == state.AS_IO1) {
state.AS_Buffer1.AB_InUse = 0;
if (state.AS_IO1->ioa_Request.io_Error != IOERR_ABORTED)
state.AS_Buffer1.AB_End = 0;
}
else {
state.AS_Buffer2.AB_InUse = 0;
if (state.AS_IO2->ioa_Request.io_Error != IOERR_ABORTED)
state.AS_Buffer2.AB_End = 0;
}
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("rw%ld", state.AS_Options.AO_Debug);
#endif
break;
case ADCMD_LOCK:
/* LOCK replied */
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("rl%ld", state.AS_Options.AO_Debug);
#endif
if (state.AS_LockIO->ioa_Request.io_Error == ADIOERR_CHANNELSTOLEN) {
/* Channel stolen, set allocate flag */
state.AS_AllocState = ALLOCSTATE_STOLEN;
/*
* If NextBuffer is in use, it is currently played,
* and CurrentBuffer is sent, but not yet played by the
* audio.device, so it is safe to AbortIO it without
* losing sound. Anyway, we wait until both io requests
* are inactive before we send the ADCMD_FREE request.
*/
if (state.AS_NextBuffer->AB_InUse)
abortIO(state.AS_CurrentIO, &(state.AS_Options));
/*
* swap, so Current becomes Next to be sent as next after
* channel reallocation.
*/
swapPtrs(&state);
}
state.AS_LockState = LOCKSTATE_UNUSED;
break;
case ADCMD_FREE:
/* Allocation has ended, set state */
state.AS_AllocState = ALLOCSTATE_UNUSED;
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("rf%ld", state.AS_Options.AO_Debug);
#endif
/* Check if we still have data to send, if yes, the FREE */
/* was a response to free stolen channels */
if (!IsListEmpty(&(state.AS_ReadyQueue))
|| state.AS_Buffer1.AB_End || state.AS_Buffer2.AB_End) {
/* try to allocate the channels again */
/* use old key */
allocIO(state.AS_AllocIO, &(state.AS_Options));
state.AS_AllocState = ALLOCSTATE_ALLOCATING;
}
break;
}
break;
default:
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("P%ld?%ld", packet->dp_Type, state.AS_Options.AO_Debug);
#endif
returnpkt(packet, DOSFALSE, ERROR_ACTION_NOT_KNOWN, &(state.AS_Options));
break;
}
}
/* If there is stealing and audio channel is no longer used, free it */
if (state.AS_AllocState == ALLOCSTATE_STOLEN
&& !state.AS_Buffer1.AB_InUse && !state.AS_Buffer2.AB_InUse) {
/* Free Channel */
freeIO(state.AS_AllocIO, &(state.AS_Options));
state.AS_AllocState = ALLOCSTATE_FREEING;
}
/* As long as there is activity, try handling input and output */
while (checkReadyQueue(&state) || checkIOBHandling(&state))
;
/* Is device being closed? */
if (!state.AS_OpenForOutput && IsListEmpty(&(state.AS_ReadyQueue))
&& state.AS_AllocState == ALLOCSTATE_ALLOCATED
&& !state.AS_Buffer1.AB_InUse && !state.AS_Buffer2.AB_InUse
&& !state.AS_Buffer1.AB_End && !state.AS_Buffer2.AB_End) {
/* Free Channel */
freeIO(state.AS_AllocIO, &(state.AS_Options));
state.AS_AllocState = ALLOCSTATE_FREEING;
}
} while (state.AS_OpenForOutput || !IsListEmpty(&(state.AS_ReadyQueue))
|| state.AS_AllocState != ALLOCSTATE_UNUSED
|| state.AS_LockState != LOCKSTATE_UNUSED);
handler_exit:
if (!error)
CloseDevice((struct IORequest *)state.AS_AllocIO);
if (state.AS_IO1)
FreeMem(state.AS_IO1, sizeof(struct IOAudio));
if (state.AS_IO2)
FreeMem(state.AS_IO2, sizeof(struct IOAudio));
if (state.AS_AllocIO)
FreeMem(state.AS_AllocIO, sizeof(struct IOAudio));
if (state.AS_LockIO)
FreeMem(state.AS_LockIO, sizeof(struct IOAudio));
if (state.AS_Buffer1.AB_Data)
FreeMem(state.AS_Buffer1.AB_Data, state.AS_Buffer1.AB_Size);
if (state.AS_Buffer2.AB_Data)
FreeMem(state.AS_Buffer2.AB_Data, state.AS_Buffer2.AB_Size);
/* end of main handler code */
#ifdef DEBUG
if (state.AS_Options.AO_Debug)
DPRINTF("X%ld\n", state.AS_Options.AO_Debug);
#endif
}
/*
* checkReadyQueue()
*
* check, if there are still incoming packets not already handled
*/
BOOL checkReadyQueue(struct AudioState *as)
{
struct Message *msg = (struct Message *)as->AS_ReadyQueue.lh_Head;
BOOL active = FALSE;
if (!as->AS_NextBuffer->AB_InUse && msg->mn_Node.ln_Succ) {
struct DosPacket *p = (struct DosPacket *) msg->mn_Node.ln_Name;
UBYTE *src, *dst;
LONG restlen, packetlen;
/* try to fit write request into current 'new'ábuffer */
restlen = as->AS_NextBuffer->AB_Size - as->AS_NextBuffer->AB_End;
if (restlen) { /* Buffer is not yet full */
packetlen = p->dp_Arg3 - as->AS_BytesCopied;
src = (UBYTE *)p->dp_Arg2 + as->AS_BytesCopied;
dst = as->AS_NextBuffer->AB_Data + as->AS_NextBuffer->AB_End;
if (packetlen <= restlen) {
/* copy whole packet and reply */
LONG i;
for (i = packetlen; i; --i)
*dst++ = *src++;
Remove(&(msg->mn_Node));
returnpkt(p, p->dp_Arg3, 0, &(as->AS_Options));
as->AS_BytesCopied = 0;
as->AS_NextBuffer->AB_End += packetlen;
}
else {
/* Buffer is full */
LONG i;
for (i = restlen; i; --i)
*dst++ = *src++;
as->AS_BytesCopied += restlen;
as->AS_NextBuffer->AB_End = as->AS_NextBuffer->AB_Size;
}
active = TRUE;
}
}
return active;
}
/*
* checkIOBHandling()
*
* check if a buffer can be sent to the audio.device
*/
BOOL checkIOBHandling(struct AudioState *as)
{
BOOL active = FALSE;
if (as->AS_AllocState == ALLOCSTATE_ALLOCATED) {
/* check if NextBuffer can be sent to the audio.device, either */
/* if the buffer is full, or the ReadyQueue is empty and the device is closing */
if (!as->AS_NextBuffer->AB_InUse
&& (as->AS_NextBuffer->AB_End == as->AS_NextBuffer->AB_Size
|| as->AS_NextBuffer->AB_End > 0 && (!as->AS_OpenForOutput)
&& IsListEmpty(&(as->AS_ReadyQueue)))) {
/* send the buffer off */
writeIO(as->AS_NextIO, as->AS_NextBuffer, &(as->AS_Options));
as->AS_NextBuffer->AB_InUse = 1;
/* swap buffers */
swapPtrs(as);
active = TRUE;
}
}
return active;
}
/*
* swapPtrs()
*
* swap two pointers
*/
void swapPtrs(struct AudioState *as)
{
struct AudioBuffer *tmpbuf;
struct IOAudio *tmpio;
tmpbuf = as->AS_NextBuffer;
as->AS_NextBuffer = as->AS_CurrentBuffer;
as->AS_CurrentBuffer = tmpbuf;
tmpio = as->AS_NextIO;
as->AS_NextIO = as->AS_CurrentIO;
as->AS_CurrentIO = tmpio;
}
/*
* allocIO()
*
* allocate one or more audio channels.
*/
void allocIO(struct IOAudio *iob, struct AudioOptions *ao)
{
iob->ioa_Request.io_Message.mn_Node.ln_Pri = ao->AO_Priority;
iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
iob->ioa_Request.io_Flags = 0;
iob->ioa_Data = ao->AO_Channels;
iob->ioa_Length = ao->AO_NumChannels;
BeginIO((struct IORequest *)iob);
#ifdef DEBUG
if (ao->AO_Debug)
DPRINTF("a%ld", ao->AO_Debug);
#endif
}
/*
* lockIO()
*
* lock one or more audio channels.
*/
void lockIO(struct IOAudio *iob, struct AudioOptions *ao)
{
iob->ioa_Request.io_Command = ADCMD_LOCK;
iob->ioa_Request.io_Flags = 0;
BeginIO((struct IORequest *)iob);
#ifdef DEBUG
if (ao->AO_Debug)
DPRINTF("l%ld", ao->AO_Debug);
#endif
}
/*
* freeIO()
*
* allocate one or more audio channels.
*/
void freeIO(struct IOAudio *iob, struct AudioOptions *ao)
{
iob->ioa_Request.io_Command = ADCMD_FREE;
iob->ioa_Request.io_Flags = 0;
BeginIO((struct IORequest *)iob);
#ifdef DEBUG
if (ao->AO_Debug)
DPRINTF("f%ld", ao->AO_Debug);
#endif
}
/*
* writeIO()
*
* allocate one or more audio channels.
*/
void writeIO(struct IOAudio *iob, struct AudioBuffer *buffer, struct AudioOptions *ao)
{
iob->ioa_Request.io_Message.mn_Node.ln_Pri = ao->AO_Priority;
iob->ioa_Request.io_Command = CMD_WRITE;
iob->ioa_Request.io_Flags = ADIOF_PERVOL;
iob->ioa_Data = buffer->AB_Data;
iob->ioa_Length = buffer->AB_End;
iob->ioa_Period = ao->AO_Period;
iob->ioa_Volume = ao->AO_Volume;
iob->ioa_Cycles = 1;
BeginIO((struct IORequest *)iob);
#ifdef DEBUG
if (ao->AO_Debug)
DPRINTF("w%ld", ao->AO_Debug);
#endif
}
/*
* abortIO()
*
* Abort a CMD_WRITE request
*/
void abortIO(struct IOAudio *iob, struct AudioOptions *ao)
{
#ifdef DEBUG
if (ao->AO_Debug)
DPRINTF("bw%ld", ao->AO_Debug);
#endif
AbortIO((struct IORequest *)iob);
}
/*
* returnpkt()
*
* return a packet to calling DOS process.
*/
void returnpkt(struct DosPacket *packet, LONG res1, LONG res2, struct AudioOptions *ao)
{
struct Message *message = packet->dp_Link;
struct MsgPort *replyport = packet->dp_Port;
struct Process *process = (struct Process *)FindTask(NULL);
packet->dp_Res1 = res1;
packet->dp_Res2 = res2;
packet->dp_Port = &(process->pr_MsgPort);
message->mn_Node.ln_Name = (char *) packet;
#ifdef DEBUG
if (ao->AO_Debug)
DPRINTF("RP%ld", ao->AO_Debug);
#endif
PutMsg(replyport,message);
}
/*
* parseArgs()
*
* parse arguments of the device string (after the colon ':')
*/
BOOL parseArgs(struct AudioOptions *ao, int len, char *str)
{
LONG val;
while (len && *str != ':') {
len--, str++;
}
if (*str != ':')
return FALSE;
len--, str++;
while (len > 0) {
if (!strncmpi(str, "PERIOD", 6)) {
len -= 6, str += 6;
val = getLong(&len, &str);
if (val >= 124 && val <= 65535)
ao->AO_Period = val;
}
else if (!strncmpi(str, "FREQUENCY", 9)) {
len -= 9, str += 9;
val = getLong(&len, &str);
if (val >= 55 && val <= 28603)
ao->AO_Period = CLOCK_CONSTANT_PAL / val;
}
else if (!strncmpi(str, "VOLUME", 6)) {
len -= 6, str += 6;
val = getLong(&len, &str);
if (val >= 0 && val <= 64)
ao->AO_Volume = val;
}
else if (!strncmpi(str, "PRIORITY", 8)) {
len -= 8, str += 8;
val = getLong(&len, &str);
if (val >= -128 && val <= 127)
ao->AO_Priority = val;
}
else if (!strncmpi(str, "BUFFER", 6)) {
len -= 6, str += 6;
val = getLong(&len, &str);
if (val >= 0x1000 && val <= 0x40000) {
val = val / 4; /* 2 buffers */
ao->AO_BufferSize = val * 2; /* must be even size */
}
}
else if (!strncmpi(str, "CHANNEL", 7)) {
len -= 7, str += 7;
val = getLong(&len, &str);
if (val >= 0 && val <= 3) {
ao->AO_NumChannels = 1;
ao->AO_Channels = "\x1\x2\x4\x8" + val;
}
}
else if (!strncmpi(str, "LEFT", 4)) {
len -= 4, str += 4;
ao->AO_NumChannels = 2;
ao->AO_Channels = "\x1\x8";
}
else if (!strncmpi(str, "RIGHT", 5)) {
len -= 5, str += 5;
ao->AO_NumChannels = 2;
ao->AO_Channels = "\x2\x4";
}
else if (!strncmpi(str, "STEREO", 6)) {
len -= 6, str += 6;
ao->AO_NumChannels = 4;
ao->AO_Channels = "\x3\x5\xA\xC";
/* but not yet fully implemented! */
}
else if (!strncmpi(str, "16BIT", 5)) {
len -= 5, str += 5;
/* not implemented! */
}
else if (!strncmpi(str, "DEBUG", 5)) {
len -= 5, str += 5;
val = getLong(&len, &str);
ao->AO_Debug = val;
}
while (len && *str != '/') /* skip rest (as CON: does, too) */
len--, str++;
if (*str == '/')
len--, str++;
}
return TRUE;
}
/*
* getLong()
*
* convert string to integer, supports decimal, octal and hex
*/
LONG getLong(int *len, char **ptr)
{
LONG val = 0;
int l = *len;
char *str = *ptr;
int sign = 1;
if (l == 0)
return val;
while (*str == ' ' || *str == '\t')
str++;
if (*str == '-') {
sign = -1;
str++;
}
if (*str == '0') {
l--, str++;
if (l == 0) {
*len = l, *ptr = str;
return val;
}
if (QTOUPPER(*str) == 'X') {
l--, str++;
while (len > 0) {
if (*str >= '0' && *str <= '9')
val = val * 16 + *str - '0';
else if (QTOUPPER(*str) >= 'A' && QTOUPPER(*str) <= 'F')
val = val * 16 + QTOUPPER(*str) - 'A';
else
break;
l--, str++;
}
}
else {
while (len > 0 && *str >= '0' && *str <= '7') {
val = val * 8 + *str - '0';
l--, str++;
}
}
}
else {
while (len > 0 && *str >= '0' && *str <= '9') {
val = val * 10 + *str - '0';
l--, str++;
}
}
*len = l, *ptr = str;
return val * sign;
}
/*
* strncmpi()
*
* compare two strings, length n, ignorecase
*/
int strncmpi(char *str1, char *str2, int n)
{
UBYTE *astr = str1;
UBYTE *bstr = str2;
UBYTE c;
while ((c = QTOUPPER(*astr)) && (c == QTOUPPER(*bstr)) && n)
astr++, bstr++, n--;
if (!c || !n)
return 0;
if (c < *bstr)
return -1;
return 1;
}